package com.fsh.lingsp.common.websocket;

import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.util.StrUtil;
import com.fsh.lingsp.common.websocket.util.NettyUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.HttpRequest;

import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.function.Function;

/**
 * 作者：fsh
 * 日期：2024/02/26
 * <p>
 * 描述：
 * // 定义一个名为MyHeaderCollectHandler的类，它继承自ChannelInboundHandlerAdapter，
 * // 用于处理入站的Channel事件。
 */
public class MyHeaderCollectHandler extends ChannelInboundHandlerAdapter {


    /**
     * 重写channelRead方法，该方法在接收到入站消息时被调用。
     *      参数ctx是ChannelHandlerContext对象，提供对当前处理链的上下文信息。
     *      参数msg是接收到的消息对象。
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 判断接收到的消息是否是HttpRequest类型。
        if (msg instanceof HttpRequest) {
            // 将消息对象强制类型转换为HttpRequest，并赋值给httpRequest变量。
            HttpRequest httpRequest = (HttpRequest) msg;
            // 使用UrlBuilder的静态方法ofHttp，根据httpRequest的URI创建一个UrlBuilder对象，用于构建或解析URL。
            UrlBuilder urlBuilder = UrlBuilder.ofHttp(httpRequest.getUri());
            // 从URL的查询参数中获取名为"token"的参数值，并转换为String类型。这样写是预防空指针。
            Optional<String> tokenOptional=Optional.of(urlBuilder)
                    .map(UrlBuilder::getQuery)
                    .map(k->k.get("token"))
                    .map(CharSequence::toString);
                // 如果token非空，则使用NettyUtil工具类的setAttr方法将token存储到当前Channel的属性中，
                // 其中NettyUtil.TOKEN是一个静态的AttributeKey对象，用于标识存储token的属性。
            tokenOptional.ifPresent(s->NettyUtil.setAttr(ctx.channel(), NettyUtil.TOKEN, s));
            //切记，要将这个参数移除，不然后面的握手处理器匹配不上这个url了。
            // url 就是完整的路径  ，path就是不携带参数的url
            httpRequest.setUri(urlBuilder.getPath().toString());


            //取出用户的ip。 这一步是针对Nginx代理请求，它会把真实ip放在这里。
            String ip = httpRequest.headers().get("X-Real-IP");
            if (StrUtil.isBlank(ip)){
                // 没有nginx代理请求，是直连的。
                InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress();
                ip = address.getAddress().getHostAddress();
            }
            //保存到channel附件中
            NettyUtil.setAttr(ctx.channel(), NettyUtil.IP, ip);

            //这个处理器只需要用一次，因为接下来就是 http升级为ws。所以可以移除。
            ctx.pipeline().remove(this);
        }
        // 调用ChannelHandlerContext的fireChannelRead方法，将消息传递给ChannelPipeline中的下一个Handler。
        // 这确保了消息在ChannelPipeline中继续传播，除非某个Handler处理并消耗了它。
        ctx.fireChannelRead(msg);
    }
}
